/**
 * @file drv_adc.c
 * @brief ADC驱动源文件
 * @version 0.1
 * @date 2024-01-30
 */
#include <rtthread.h>
#include "n32g031.h"
#include "drv_adc.h"
#include "bsp_config.h"
#ifdef RT_USING_ADC
/* ADC设备结构体 */
struct n32_adc
{
    ADC_Module* adc_periph;    /* ADC外设地址 */
    DMA_Module* dma_periph;     /* DMA外设地址 */
    uint32_t dma_channel;       /* DMA通道号 */
    struct adc_config config;   /* ADC配置 */
    rt_bool_t is_busy;          /* 设备忙标志 */
};

/* ADC设备实例 */
static struct n32_adc adc = {
    ADC,                /* ADC外设地址 */
    DMA,                /* DMA外设地址 */
    DMA_CH1,           /* DMA通道号 */
    {0},               /* ADC配置 */
    RT_FALSE,          /* 设备忙标志 */
};

/**
 * @brief ADC GPIO初始化
 */
static void n32_adc_gpio_init(void)
{
    GPIO_InitType GPIO_InitStructure;

    /* 使能GPIO时钟 */
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA, ENABLE);

    /* 配置ADC GPIO */
    GPIO_InitStruct(&GPIO_InitStructure);
    GPIO_InitStructure.Pin = GPIO_PIN_0 | GPIO_PIN_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_ANALOG;
    GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);
}

/**
 * @brief ADC DMA初始化
 */
static void n32_adc_dma_init(void)
{
    DMA_InitType DMA_InitStructure;

    /* 使能DMA时钟 */
    RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA, ENABLE);

    /* 配置DMA */
    DMA_DeInit(DMA_CH1);
    DMA_InitStructure.PeriphAddr     = (uint32_t)&ADC->DAT;
    DMA_InitStructure.MemAddr        = 0;
    DMA_InitStructure.Direction      = DMA_DIR_PERIPH_SRC;
    DMA_InitStructure.BufSize        = 0;
    DMA_InitStructure.PeriphInc      = DMA_PERIPH_INC_DISABLE;
    DMA_InitStructure.DMA_MemoryInc  = DMA_MEM_INC_ENABLE;
    DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_HALFWORD;
    DMA_InitStructure.MemDataSize    = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.CircularMode   = DMA_MODE_NORMAL;
    DMA_InitStructure.Priority       = DMA_PRIORITY_HIGH;
    DMA_InitStructure.Mem2Mem        = DMA_M2M_DISABLE;
    DMA_Init(DMA_CH1, &DMA_InitStructure);
}

/**
 * @brief ADC初始化
 */
static void n32_adc_init(void)
{
    ADC_InitType ADC_InitStructure;

    /* 使能ADC时钟 */
    RCC_EnableAPB2PeriphClk(RCC_AHB_PERIPH_ADC, ENABLE);

    /* ADC配置 */
    ADC_InitStructure.MultiChEn      = adc.config.scan_mode;
    ADC_InitStructure.ContinueConvEn = adc.config.continuous_mode;
    ADC_InitStructure.ExtTrigSelect  = adc.config.external_trig_conv;
    ADC_InitStructure.DatAlign       = adc.config.data_align;
    ADC_InitStructure.ChsNumber      = 1;
    ADC_Init(adc.adc_periph, &ADC_InitStructure);

    /* 使能ADC */
    ADC_Enable(adc.adc_periph, ENABLE);

    /* 校准ADC */
    ADC_StartCalibration(adc.adc_periph);
    while(ADC_GetCalibrationStatus(adc.adc_periph));
}

/**
 * @brief ADC驱动初始化
 * @return int 0:成功
 */
int adc_init(void)
{
    /* GPIO初始化 */
    n32_adc_gpio_init();

    /* DMA初始化 */
    n32_adc_dma_init();

    /* ADC初始化 */
    n32_adc_init();

    return RT_EOK;
}

/**
 * @brief 配置ADC通道
 * @param device ADC设备句柄
 * @param config ADC通道配置
 * @return int 0:成功, <0:错误码
 */
int adc_config_channel(AdcDevice device, struct adc_channel_config *config)
{
    if(device != 0 || config == RT_NULL)
        return RT_ADC_ERR_INVALID_PARAM;

    if(adc.is_busy)
        return RT_ADC_ERR_BUSY;

    /* 配置ADC通道采样时间 */
    ADC_ConfigRegularChannel(adc.adc_periph, config->channel, 1, config->sample_time);

    return RT_EOK;
}

/**
 * @brief 启动ADC采样
 * @param device ADC设备句柄
 * @return int 0:成功, <0:错误码
 */
int adc_start(AdcDevice device)
{
    if(device != 0)
        return RT_ADC_ERR_INVALID_PARAM;

    if(adc.is_busy)
        return RT_ADC_ERR_BUSY;

    adc.is_busy = RT_TRUE;

    /* 启动ADC转换 */
    ADC_EnableSoftwareStartConv(adc.adc_periph, ENABLE);

    return RT_EOK;
}

/**
 * @brief 停止ADC采样
 * @param device ADC设备句柄
 * @return int 0:成功, <0:错误码
 */
int adc_stop(AdcDevice device)
{
    if(device != 0)
        return RT_ADC_ERR_INVALID_PARAM;

    /* 停止ADC转换 */
    ADC_EnableSoftwareStartConv(adc.adc_periph, DISABLE);

    adc.is_busy = RT_FALSE;

    return RT_EOK;
}

/**
 * @brief 读取ADC采样数据
 * @param device ADC设备句柄
 * @param buffer 数据缓冲区
 * @param length 数据长度
 * @return int 0:成功, <0:错误码
 */
int adc_read(AdcDevice device, uint16_t *buffer, uint32_t length)
{
    if(device != 0 || buffer == RT_NULL || length == 0)
        return RT_ADC_ERR_INVALID_PARAM;

    if(adc.is_busy)
        return RT_ADC_ERR_BUSY;

    if(adc.config.dma_enable)
    {
        /* 配置DMA */
        DMA_SetMemoryAddress(adc.dma_periph, adc.dma_channel, (uint32_t)buffer);
        DMA_SetBufferSize(adc.dma_periph, adc.dma_channel, length);

        /* 使能DMA */
        DMA_EnableChannel(adc.dma_periph, adc.dma_channel, ENABLE);

        /* 使能ADC DMA */
        ADC_EnableDMA(adc.adc_periph, ENABLE);

        /* 启动ADC转换 */
        ADC_EnableSoftwareStartConv(adc.adc_periph, ENABLE);

        /* 等待DMA传输完成 */
        while(!DMA_GetFlagStatus(DMA_FLAG_TC1, adc.dma_periph));

        /* 清除DMA标志 */
        DMA_ClearFlag(DMA_FLAG_TC1, adc.dma_periph);

        /* 禁止DMA */
        DMA_EnableChannel(adc.dma_periph, adc.dma_channel, DISABLE);

        /* 禁止ADC DMA */
        ADC_EnableDMA(adc.adc_periph, DISABLE);
    }
    else
    {
        uint32_t i;

        for(i = 0; i < length; i++)
        {
            /* 启动ADC转换 */
            ADC_EnableSoftwareStartConv(adc.adc_periph, ENABLE);

            /* 等待转换完成 */
            while(!ADC_GetFlagStatus(adc.adc_periph, ADC_FLAG_ENDC));

            /* 读取转换结果 */
            buffer[i] = ADC_GetDat(adc.adc_periph);

            /* 清除转换完成标志 */
            ADC_ClearFlag(adc.adc_periph, ADC_FLAG_ENDC);

            /* 等待一段时间 */
            rt_thread_mdelay(1);
        }
    }

    return RT_EOK;
}
#endif
